﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;

namespace Logger
{
    public partial class FormPlayer : Form
    {
        double scale = 3;

        class ScreenDataFile
        {
            public DateTime dt;
            public string fPath;
            public ScreenDataFile(DateTime actDate, String fPath)
            {
                this.fPath = fPath;
                FileInfo fi = new FileInfo(fPath);
                String s = actDate.ToString("dd.MM.yyyy ") + fi.Name.Substring(4, fi.Name.Length - 4 - 3).Replace('_', ':');
                dt = Convert.ToDateTime(s);
            }
        }

        List<ScreenData> scrL = new List<ScreenData>();
        List<ScreenDataFile> sfl = new List<ScreenDataFile>();

        public FormPlayer()
        {
            InitializeComponent();
            imLine.BackgroundImage = new Bitmap(imLine.Width, imLine.Height);
            imLine.Image = new Bitmap(imLine.Width, imLine.Height);
            // загрузка текущей даты
            LoadRecData(cld.Value);
            ReDrawTimeLine();
        }

        void LoadRecData(DateTime loadDt)
        {
            sfl.Clear();
            actF = 0;
            string dPath = Gl.dPathScreen + "\\" + loadDt.ToString("yyyy_MM_dd");
            if (!Directory.Exists(dPath)) { SetEnabled(); return; }

            string[] fl = Directory.GetFiles(dPath, "*.dt");
            foreach (string f in fl)
            {
                sfl.Add(new ScreenDataFile(loadDt, f));
            }

            if (sfl.Count > 0)
            {
                NewRecFile(0);
            }

            SetEnabled();
        }

        void SetEnabled()
        {
            tBtnPlay.Enabled = sfl.Count > 0;
            tBtnJampBk.Enabled = sfl.Count > 0;
            tBtnJampFr.Enabled = sfl.Count > 0;
        }

        void ReDrawTimeLine()
        {
            Graphics gr = Graphics.FromImage(imLine.BackgroundImage);
            gr.Clear(Color.LightGray);
            double kMin = imLine.Width / 1440.0; // коэффициент. Сколько пикселей в одной минуте
            int wPix = imLine.Image.Width <= 1440 ? 1 : 2; // размер одной минуты на линейки в пикселях
            int x;

            if (sfl.Count > 0)
            {
                // данные
                for (int i = 0; i < sfl.Count; i++)
                {
                    x = (int)((sfl[i].dt - sfl[i].dt.Date).TotalMinutes * kMin);
                    gr.FillRectangle(Brushes.White, x, 5, wPix, imLine.Height - 27 - 5);
                }
            }

            // линейка
            gr.DrawLine(Pens.Black, 0, imLine.Height - 25, imLine.Width, imLine.Height - 25);
            for (int i = 0; i < 24; i++)
            {
                x = (int)(i * 60 * kMin);
                if (i > 0)
                {
                    gr.DrawLine(Pens.Black, x, imLine.Height - 25, x, imLine.Height - 17);
                    gr.DrawString(i.ToString("00"), Font, Brushes.Black, x - 7, imLine.Height - 15);
                }
                x = (int)((i * 60 + 30) * kMin);
                gr.DrawLine(Pens.Black, x, imLine.Height - 25, x, imLine.Height - 20);
            }

            gr.Dispose();

            DrawTimeLine();
        }

        void DrawTimeLine()
        {
            Graphics gr = Graphics.FromImage(imLine.Image);
            gr.Clear(Color.Transparent);

            double kMin = imLine.Width / 1440.0; // коэффициент. Сколько пикселей в одной минуте

            if (sfl.Count > 0)
            {
                // курсор
                int x = (int)((scrL[actS].dt - scrL[actS].dt.Date).TotalMinutes * kMin);
                gr.DrawLine(Pens.Maroon, x, 5, x, imLine.Height - 27);
                gr.DrawLine(Pens.Yellow, x + 1, 5, x + 1, imLine.Height - 27);
            }

            gr.Dispose();
            imLine.Refresh();
        }

        void LoadData(string fPath)
        {
            scrL.Clear();
            actS = 0;
            byte[] buf = File.ReadAllBytes(fPath);
            MemoryStream ms = new MemoryStream(YCmpr.Decompress(buf));
            BinaryReader br = new BinaryReader(ms);
            #region*** запись данных в массив *************
            int kolVo = br.ReadInt32(); // количество кадров
            byte[] ind = null;
            for (int i = 0; i < kolVo; i++)
            {
                DateTime dt = new DateTime(br.ReadInt64()); // время
                int w = br.ReadInt32(); // ширина
                int h = br.ReadInt32(); // высота
                int lenPart = br.ReadInt32(); // длина одной части
                int lenLastPart = br.ReadInt32(); // длина последней части
                int pLen = br.ReadInt32(); // количество частей
                if (i == 0) ind = new byte[pLen];
                Part[] prtL = new Part[pLen];
                for (int p = 0; p < pLen; p++)
                {
                    prtL[p] = new Part();
                    bool bNewPart = br.ReadByte() == 1; // существует ли часть
                    if (bNewPart)
                    {
                        ind[p] = (byte)i;
                        prtL[p].mas = br.ReadBytes(((p < pLen - 1) ? lenPart : lenLastPart));// сама часть
                    }
                }
                ScreenData scr = new ScreenData(prtL, (byte[])ind.Clone(), lenPart, lenLastPart, w, h, dt);
                scrL.Add(scr);
            }
            #endregion*** запись данных в массив *********

            br.Close();
            ms.Close();
            ms.Dispose();

            GC.Collect();
        }

        int actS = 0;
        int actF = 0;
        bool bShowScreen = true;
        double ElapsedSec = 0;
        private void tmrMain_Tick(object sender, EventArgs e)
        {
            tmrMain.Stop();
            ElapsedSec += ESpace.Utility.GetElapsedTime() * (tsbSpeed.Value / 2.0);

            // если еще есть кадры
            if (actS < (scrL.Count - 1))
            {
                double totalSec = (scrL[actS + 1].dt - scrL[actS].dt).TotalSeconds;
                // если достигнуто время следующего кадра, переходим на следующий кадр
                while (ElapsedSec >= totalSec)
                {
                    ElapsedSec -= totalSec;
                    bShowScreen = true;
                    if (actS < (scrL.Count - 1)) actS++;
                    else break;
                }
            }
            // если кадров больше нет
            else
            {
                // если есть еще записи за эти сутки
                if (actF < (sfl.Count - 1))
                {
                    NewRecFile(actF + 1);
                    bShowScreen = true;
                }
                // если записи за эти сутки закончены
                else
                {
                    tBtnStop_Click(null, null);
                    return;
                }
            }

            // вывод кадра на экран
            if (bShowScreen)
            {
                if (im.Image != null) im.Image.Dispose();
                Bitmap bmp = MasToBmp16(scrL[actS].GetScreen(scrL), scrL[actS].w, scrL[actS].h);
                if (scale > 1)
                {
                    Bitmap bmp1 = new Bitmap(bmp, (int)(scrL[actS].w / scale), (int)(scrL[actS].h / scale));
                    bmp.Dispose();
                    bmp = bmp1;
                }
                im.Image = bmp;
                Text = actS.ToString();
                bShowScreen = false;
                lblTime.Text = scrL[actS].dt.ToString("dd.MM.yyyy HH.mm.ss");
            }

            tmrMain.Start();
        }

        Bitmap MasToBmp16(byte[] buf, int w, int h)
        {
            Bitmap bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);

            // Блокируем битмап с копированием его данных в массив 
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
            IntPtr ptr = bmpData.Scan0;
            int bytes = bmpData.Stride * bmp.Height;

            System.Runtime.InteropServices.Marshal.Copy(buf, 0, ptr, buf.Length); // Copy 
            bmp.UnlockBits(bmpData); // Unlock 

            return bmp;
        }

        private void imLine_MouseDown(object sender, MouseEventArgs e)
        {
            tmrMain.Stop();

            if (sfl.Count == 0) return;

            double kMin = imLine.Width / 1440.0; // коэффициент. Сколько пикселей в одной минуте
            // выбранное время в секундах
            int sec = (int)(e.X / kMin * 60.0);

            // смотрим, к какой записи ближе
            int ind = -1;
            for (int i = 0; i < sfl.Count; i++)
            {
                if (i == (sfl.Count - 1)) { ind = sfl.Count - 1; break; }

                int s2 = (int)(sfl[i + 1].dt - sfl[i + 1].dt.Date).TotalSeconds;
                if (sec > s2) continue;

                int s1 = (int)(sfl[i].dt - sfl[i].dt.Date).TotalSeconds;

                if (Math.Abs(s1 - sec) <= Math.Abs(s2 - sec)) ind = i;
                else ind = i + 1;
                break;
            }

            NewRecFile(ind);
            if(tBtnStop.Visible) tmrMain.Start();
        }

        private void tBtnPlay_Click(object sender, EventArgs e)
        {
            tBtnPlay.Visible = false;
            tBtnStop.Visible = true;
            bShowScreen = true;
            ESpace.Utility.GetElapsedTime();
            ElapsedSec = 0;
            tmrMain.Start();
        }

        void NewRecFile(int ind)
        {
            if (sfl.Count > 0)
            {
                actF = ind;
                LoadData(sfl[actF].fPath);
                actS = 0;
                ESpace.Utility.GetElapsedTime();
                ElapsedSec = 0;
                DrawTimeLine();
            }
        }

        private void tBtnScaleUp_Click(object sender, EventArgs e)
        {
            if (scale > 1) { scale -= 0.25; tBtnScaleDown.Enabled = true; }
            if (scale <= 1) { scale = 1; tBtnScaleUp.Enabled = false; }
        }

        private void tBtnScaleDown_Click(object sender, EventArgs e)
        {
            if (scale < 4) { scale += 0.25; tBtnScaleUp.Enabled = true; }
            if (scale >= 4) { scale = 4; tBtnScaleDown.Enabled = false; }
        }

        public delegate void ShowAndActivateDelegate();
        public void ShowAndActivate()
        {
            WindowState = FormWindowState.Normal;
            Activate();
        }

        public void CloseForm()
        {
            Close();
        }

        private void FormPlayer_Resize(object sender, EventArgs e)
        {
            imLine.Image.Dispose();
            imLine.BackgroundImage.Dispose();
            imLine.Image = new Bitmap(imLine.Width, imLine.Height);
            imLine.BackgroundImage = new Bitmap(imLine.Width, imLine.Height);
            ReDrawTimeLine();
        }

        private void cld_ValueChanged(object sender, EventArgs e)
        {
            tBtnStop_Click(null, null);
            LoadRecData(cld.Value);
            im.Image = new Bitmap(10, 10);
            im.Refresh();
            ReDrawTimeLine();
        }

        private void tBtnStop_Click(object sender, EventArgs e)
        {
            tBtnPlay.Visible = true;
            tBtnStop.Visible = false;
            tmrMain.Stop();
        }

        private void tBtnJampBk_Click(object sender, EventArgs e)
        {
            if (actF >= 3) { NewRecFile(actF - 3); }
        }

        private void tBtnJampFr_Click(object sender, EventArgs e)
        {
            if (actF < (sfl.Count - 3)) { NewRecFile(actF + 3); }
        }

        private void tsbSpeed_Scroll(object sender, EventArgs e)
        {
            lblSpeed.Text = (tsbSpeed.Value / 2.0).ToString("F1") + "x";
        }

    }
}
